package com.wr.factory;

import com.wr.annotation.*;
import com.wr.utils.ClassUtil;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author 王瑞
 * @descreption:  初始化自定义IOC容器的类
 * @date 2020/2/15  0:17
 */
public class BeanFactory {
    //自定义一级缓存map 用于存放最终被存放在ioc容器中的bean
    private static Map<String, Object> cacheOneMap = new HashMap<>();

    //自定义二级缓存map 用于存放待处理的bean
    private static Map<String, Object> cacheTwoMap = new HashMap<>();

    //自定义三级缓存map 用于存放刚刚被初始化 还未解决注入依赖的bean
    private static Map<String, Object> cacheThreeMap = new HashMap<>();

    //定义一个常量的Class数组 用来存放需要判断哪些类添加了的指定注解的注解类对象 如： 注解Component  注解CRepository 注解CService
    private static final Class<?>[] BEAN_ANNOTATIONS = {Component.class, Repository.class, Service.class};

    //系统初始化时 需要被扫描的包名
    private static final String SCAN_PACKAGE = "com.wr";

    static {
        //扫描指定包下的所有类，获取该包下的所有类的Class对象
        List<Class<?>> allClass = ClassUtil.getAllClassByPackageName(SCAN_PACKAGE);
        try {
            //初始化第三级缓存的map  也就是cacheThreeMap  用于存放刚刚被初始化 还未解决注入依赖的bean
            initCacheThreeMap(allClass);

            //初始化二级缓存map 此时的bean依赖关系已经处理完毕
            initCacheTwoMap();

            //初始化一级缓存map 也就是最终的IOC容器 此时的bean依赖关系已经处理完毕 并已经对被@Transation标记过的类生成了动态代理类 最终的Bean 都存放在这个一级缓存中
            initCacheOneMap();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    //这个方法用来判断一个类是否在类上添加了Component，Repository，Service中的任意一个注解
    public static Annotation hasConditionAnnotationClass(Class<?> aClass) {
        //利用反射获得该类的所有注解对象
        Annotation[] annotations = aClass.getAnnotations();
        for (Annotation annotation : annotations) {
            //遍历这些注解并获得每一个注解的注解类型的类对象
            Class<? extends Annotation> aClass1 = annotation.annotationType();

            for (Class<?> aClass2 : BEAN_ANNOTATIONS) {
                //遍历需要判断的注解常量数组 与当前类的注解一一做比较
                // 如果发现该类的注解中包含 Component，Repository，Service任意一个 这个注解对象
                if (aClass2.equals(aClass1)) {
                    return annotation;
                }
            }
        }

        return null;
    }

    //这个方法用来判断一个类是否在类上添加了@Transactional注解
    public static boolean hasTransationalAnnotationClass(Class<?> aClass) {
        //利用反射获得该类的所有注解对象
        Annotation[] annotations = aClass.getAnnotations();
        Class<Transactional> aClass2 = Transactional.class;
        for (Annotation annotation : annotations) {
            //遍历这些注解并获得每一个注解的注解类型的类对象
            Class<? extends Annotation> aClass1 = annotation.annotationType();
            if (aClass2.equals(aClass1)) {
                return true;
            }
        }
        return false;
    }

    //获得一个类上的指定注解的参数值
    public static String getAnnotationValue(Class<?> aClass, Annotation annotation) throws Exception {
        //利用反射获取类的方法对象的数组
        Method[] declaredMethods = aClass.getDeclaredMethods();
        String value = null;
        for (Method method : declaredMethods) {
            String name = method.getName();
            //获得指定方法的参数值
            if ("value".equals(name)) {
                String invoke = (String) method.invoke(annotation, null);
                if ((!"".equals(invoke)) && (invoke != null)) {
                    value = invoke;
                }
            }
        }
        return value;
    }

    //初始化三级缓存  把被指定的注解标记过的类 添加到三级缓存map中  以待处理
    public static void initCacheThreeMap(List<Class<?>> allClass) {
        for (Class<?> aClass : allClass) {
            //判断这些类中哪些类上添加了 注解Component 、 注解Repository、 注解CService 中的任意一个 如果有 就返回这个注解类对象
            Annotation annotation = hasConditionAnnotationClass(aClass);
            if (annotation != null) {
                try {
                    //获取这个注解类对象
                    Class<? extends Annotation> aClass1 = annotation.annotationType();

                    //利用反射获取注解内的参数值
                    String annotationValue = getAnnotationValue(aClass1, annotation);

                    //如果该类添加了指定的任意一个注解 就用反射实例化这个类 并添加到一级缓存
                    Object o = aClass.getDeclaredConstructor().newInstance();
                    //如果注解内的value参数有值，就用这个值来做map的KEY
                    if (annotationValue != null) {
                        cacheThreeMap.put(annotationValue, o);
                    } else {
                        //如果注解内的value参数没有传值，就用当前类的类名（首字母小写） 来做map的KEY
                        String classNameByClass = getClassNameByClass(aClass);
                        cacheThreeMap.put(classNameByClass, o);

                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }

    }

    //初始化二级缓存  处理各个bean的依赖关系 以备最终放入到一级缓存 也就是最终的容器中
    public static void initCacheTwoMap() throws Exception {
        Set<String> strings = cacheThreeMap.keySet();
        //遍历三级缓存
        for (String string : strings) {
            Object obj = cacheThreeMap.get(string);
            //获得每一个被放入三级缓存的类的类对象
            Class<?> aClass = obj.getClass();
            //获取该类的所有属性字段
            Field[] fields = aClass.getDeclaredFields();
            for (Field field : fields) {
                //判断该属性字段是否被@Autowire注解标记过
                boolean isAutowired = hasConditionAnnotationField(field);
                if (isAutowired) {
                    //如果被@Autowire标记过
                    setValue(field, obj, aClass);
                }
            }
            //前面是将三级缓存中取出的对象处理依赖注入
            //处理完毕之后放入二级缓存
            cacheTwoMap.put(string, obj);
        }
    }

    //初始化一级缓存  处理各个bean的依赖关系 最终放入到一级缓存 也就是最终的容器中
    public static void initCacheOneMap() throws Exception {
        Set<String> strings = cacheTwoMap.keySet();
        //从二级缓存中拿出动态代理工厂类
        ProxyInstanceFactory proxyInstanceFactory = getBeanFromCacheTwo(ProxyInstanceFactory.class);
        //遍历二级缓存
        for (String string : strings) {
            Object obj = cacheThreeMap.get(string);
            Object proxyObj = getProxyObj(obj,proxyInstanceFactory);
            cacheOneMap.put(string, proxyObj);


        }
    }

    //判断一个类是否实现过接口
    public static Boolean isImpl(Class<?> aClass) {
        Class<?>[] interfaces = aClass.getInterfaces();
        if (interfaces.length > 0) {
            return true;
        }
        return false;
    }

    //获取一个类的类名（字母小写）
    public static String getClassNameByClass(Class<?> aClass) {
        String name = aClass.getName();
        int i = name.lastIndexOf(".");
        String substring = name.substring(i + 1, name.length());
        return substring.toLowerCase();

    }

    //把被@Transatonal标记过的类生成动态代理类
    public static Object getProxyObj(Object obj,ProxyInstanceFactory proxyInstanceFactory) throws InstantiationException, IllegalAccessException {
        Class<?> aClass = obj.getClass();
        //判断该类有没有实现过接口(以便选择jdk代理或者cglib代理)
        Boolean impl = isImpl(aClass);
        Object o1 = new Object();

        //判断类上面有没有被@Transational注解标记
        boolean hasTransationalAnnotation = hasTransationalAnnotationClass(aClass);
        //如果被@Transational注解标记过 就将这个对象成成动态代理对象 返回出去  反之 直接把该对象返回出去
        if (hasTransationalAnnotation){
            if (impl) {
                o1 = proxyInstanceFactory.creatProxyInstanceJdk(obj);   //jdk动态代理
            } else {
                o1 = proxyInstanceFactory.creatProxyInstanceCglib(obj);  //cglib动态代理
            }
        }else {
            o1 = obj;
        }


        return o1;
    }

    //私有化一个内部静态方法  负责从二级缓存中取出需要使用的bean 这里主要为了取出已经放入二级缓存的ProxyInstanceFactory
    private static <T> T getBeanFromCacheTwo(Class<T> aClass){
        String classNameByClass = getClassNameByClass(aClass);
        Object o = cacheTwoMap.get(classNameByClass);
        return (T)o;
    }

    //利用反射给 被@Autowire标记过的属性赋值
    public static void setValue(Field field, Object obj, Class aClass) throws Exception {
        //如果该字段被@Autowire注解标记过   获取该属性的参数类型的类的类对象
        Class<?> classType = field.getType();
        //然后到一级缓存中匹配到和这个属性字段相同数据类型的对象
        Object objByClassType = getObjByClassType(classType);

        if (objByClassType != null) {
            //利用反射为属性赋值 此时必须要求被赋值的属性在对应得类中提供set方法 否则抛出异常
            Method[] declaredMethods = aClass.getDeclaredMethods();
            String name1 = field.getName();

            //检索对field属性是否提供set方法的编辑
            boolean hasSetMethod = false;
            for (Method method : declaredMethods) {
                String methodName = method.getName();
                if (methodName.equalsIgnoreCase("set" + name1)) {
                    //如提供了set方法 标记为true
                    hasSetMethod = true;
                    //反射赋值
                    method.invoke(obj, objByClassType);
                    break;
                }
            }
            //此时必须要求被赋值的属性在对应得类中提供set方法 否则抛出异常
            if (!hasSetMethod) {
                throw new Exception("未对 '" + aClass.getName() + "' 类的 '" + field.getName() + "'属性提供set方法");
            }
        }


    }

    //如果一个属性字段被标记了@Autowire 这里根据属性的类型完成注入 返回需要被注入的对象
    //去三级缓存中去找到与这个属性的类类型相同的对象  把这个对象返回回去 如果被注入的属性是个接口  就去三级缓存找与这个接口的显现类相同的类
    public static Object getObjByClassType(Class<?> classType) {
        Object object = null;
        Set<String> strings = cacheThreeMap.keySet();
        //遍历三级缓存
        for (String string : strings) {
            Object o = cacheThreeMap.get(string);
            Class<?> aClass = o.getClass();
            //如果三级缓存中的对象类型与 传进来的对象类型相同 就把这个对象从三级缓存中取出来返回出去
            if (classType.equals(aClass)) {
                return o;
            } else {
                //如果传进来的参数类型与 从三级缓存中取出来的对象的父接口的对象类型相同  也可以把这个对象从缓存中取出 返回出去
                Class<?>[] interfaces = aClass.getInterfaces();
                for (Class<?> anInterface : interfaces) {
                    if (classType.equals(anInterface)) {
                        return o;
                    }
                }
            }
        }
        return object;
    }


    //判断一个类的某个属性字段是否被@Autowired注解标记过
    public static boolean hasConditionAnnotationField(Field declaredField) {
        //获得该属性字段的所有注解对象数组
        Annotation[] annotations = declaredField.getAnnotations();
        for (Annotation annotation : annotations) {
            //遍历这个数组   看看有没有@Autowired注解  如果有 返回true
            Class<? extends Annotation> aClass1 = annotation.annotationType();
            Class<Autowired> autowiredClass = Autowired.class;
            if (aClass1.equals(autowiredClass)) {
                return true;
            }
        }
        return false;
    }


    //getBean方法一  根据注解的value值来获取bean
    public static Object getBean(String beanValue) throws Exception {
        Object o = cacheOneMap.get(beanValue);
        if (o == null) {
            throw new Exception("请填写正确的beanId");
        }
        return o;
    }

    //getBean方法二  根据映射类对象来获取Bean
    public static <T> T getBean(Class<T> aClass) throws Exception {
        String classNameByClass = getClassNameByClass(aClass);
        Object o = cacheOneMap.get(classNameByClass);
        return (T)o;
    }



}


